<?php

namespace App\Listeners;
//文档http://laravelacademy.org/post/6877.html
use App\Events\EvevtQueueLab;
//如果你需要手动访问底层队列任务的 delete 和 release 方法，在生成的监听器中，
//Illuminate\Queue\InteractsWithQueue trait 为这两个方法提供了访问权限
use Illuminate\Queue\InteractsWithQueue;
//要指定某个监听器需要放到队列，只需要让监听器类实现 ShouldQueue 接口即可
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Log;
//事件监听器队列,确保已经配置好队列并且在服务器或本地环境启动一个队列监听器
//通过 Artisan 命令 event:generate 生成的监听器类已经将这个接口导入当前命名空间，所有你可以直接拿来使用
//如果通过队列执行监听器的时候没有抛出任何异常，队列任务会在执行完成后被自动删除。
//注: 抛出异常的队列会一直执行同一个队列任务
class QueueListener implements ShouldQueue
{
    use InteractsWithQueue;
    /* 要自定义事件监听器使用的队列连接和队列名称，可以在监听器类中定义$connection和$queue属性@var string */

    //队列的客户端类型 任务将被推送到的连接名称.
    public $connection = 'redis';
    //队列的名称 在redis中,会以queues:listener自动命名
    public $queue = 'listeners';

    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     *
     * @param  EvevtQueueLab  $event
     * @return void
     */
    public function handle(EvevtQueueLab $event)
    {
        //会无限的调用队列
        /*if(ture){
        //这个任务会在30秒后再次调用这个队列任务,不会中断后面的程序,release应该加条件和delete()一起使用
        $this->release(30);
        }*/

        //即使被dd掉了attempts的值还是会累计
        $number = $this->attempts();
        $total = 4;
        Log::info("对象的lab值:{$event->lab}");
        Log::info("最大发送任务数==调用数:{$total}");
        //队列运行时,每次都会实时调用handle,中途程序改变这些是可以改变下一次运行的结果,*但是已经release除外,还未release会从实时调用* <- 亲测星号区域是错误的
        //以下两个都是错的猜测
        //例如限制30次,每2秒执行的,在第一个队列开始运行后的49秒改成限制25次,此刻第25个任务已经被release,第25个任务运行的时候,还是30次的限制,会再次释放第26个任务,但是第26个任务就是30次了,要测试可以改成5S或者更长时间,次数调小点
        //测试数据间隔10s,初始最大10,0第二个任务已发出,10第三个任务,20第四任务发出,第20-30秒改成最大4,看是否发送第五个任务,发了结论正确,[然而测试结果是没有发出去,所以发出去的也是实时读取,并没有类似保存代码一说,release的对象是从一而终,代码确是实时读取的]

        //对 $event操作并不会影响下一个队列任务的$event对象
        $event->lab += 1;

        $this->delete();
        if($number < $total){//释放$total个任务
            Log::info("设置最大循环次数{$total},队列{$event->lab} 这是 $number 次调用");
            $add = $number + 1;
            //用release释放的任务共用同一个attempts()数累加
            //通过release已经释放的任务,是不会受实时代码影响的
            Log::info("第 {$add} 任务已发出");
            $this->release(10);
        }else{
            Log::info("设置最大循环次数{$total},队列{$event->lab} 这是 $number 次调用 END不发任务了");
        }
        //并不会影响artisan:queue的其他队列,但是会重复一直循环,可能因为无法执行删除的指令,除非执行以上的delete()
        dd('并没有结束');

        /**即使被dd掉了attempts的值还是会累计  退出指令queue:listen,只要不重启redis,下一次进行时还是会继续上次未执行的残留的队列任务
        [2017-09-19 09:11:46] local.INFO: 对象的lab值:123
        [2017-09-19 09:11:46] local.INFO: 最大发送任务数==调用数:4
        [2017-09-19 09:11:46] local.INFO: 设置最大循环次数4,队列124 这是 1 次调用
        [2017-09-19 09:11:46] local.INFO: 第 2 任务已发出
        [2017-09-19 09:11:58] local.INFO: 对象的lab值:123
        [2017-09-19 09:11:58] local.INFO: 最大发送任务数==调用数:4
        [2017-09-19 09:11:58] local.INFO: 设置最大循环次数4,队列124 这是 2 次调用
        [2017-09-19 09:11:58] local.INFO: 第 3 任务已发出
        [2017-09-19 09:12:11] local.INFO: 对象的lab值:123
        [2017-09-19 09:12:11] local.INFO: 最大发送任务数==调用数:4
        [2017-09-19 09:12:11] local.INFO: 设置最大循环次数4,队列124 这是 3 次调用
        [2017-09-19 09:12:11] local.INFO: 第 4 任务已发出
        [2017-09-19 09:12:23] local.INFO: 对象的lab值:123
        [2017-09-19 09:12:23] local.INFO: 最大发送任务数==调用数:4
        [2017-09-19 09:12:23] local.INFO: 设置最大循环次数4,队列124 这是 4 次调用 END不发任务了
        [2017-09-19 09:13:53] local.INFO: 对象的lab值:123
        [2017-09-19 09:13:53] local.INFO: 最大发送任务数==调用数:4
        [2017-09-19 09:13:53] local.INFO: 设置最大循环次数4,队列124 这是 5 次调用 END不发任务了
        [2017-09-19 09:15:25] local.INFO: 对象的lab值:123
        [2017-09-19 09:15:25] local.INFO: 最大发送任务数==调用数:4
        [2017-09-19 09:15:25] local.INFO: 设置最大循环次数4,队列124 这是 6 次调用 END不发任务了
         *
         *
         * 重启之后
         * [2017-09-19 09:44:23] local.INFO: 对象的lab值:123
        [2017-09-19 09:44:23] local.INFO: 最大发送任务数==调用数:4
        [2017-09-19 09:44:23] local.INFO: 设置最大循环次数4,队列124 这是 1 次调用
        [2017-09-19 09:44:23] local.INFO: 第 2 任务已发出
        [2017-09-19 09:44:24] local.INFO: 对象的lab值:234
        [2017-09-19 09:44:24] local.INFO: 最大发送任务数==调用数:4
        [2017-09-19 09:44:24] local.INFO: 设置最大循环次数4,队列235 这是 1 次调用
        [2017-09-19 09:44:24] local.INFO: 第 2 任务已发出
        [2017-09-19 09:44:25] local.INFO: 对象的lab值:123
        [2017-09-19 09:44:25] local.INFO: 最大发送任务数==调用数:4
        [2017-09-19 09:44:25] local.INFO: 设置最大循环次数4,队列124 这是 7 次调用 END不发任务了
         */
    }

    /**
     * 处理失败任务
     * 有时候队列中的事件监听器可能会执行失败。如果队列中的监听器任务执行时超出了队列进程定义的最大尝试次数，监听器上的failed方法会被调用，failed方法接收事件实例和造成失败的异常
     */
    public function failed(EvevtQueueLab $event){

    }

    /**
     * 处理用户登录事件
     */
    public function onUserLogin($event){}

    /**
     * 处理用户退出事件
     */
    public function onUserLogout($event){}

    public function onLab($event){
        $content = json_encode($event);
        Log::info($content);
    }

    /**
     * 事件订阅者 这个类是个事件订阅者
     * 没有使用 EventServiceProvider 提供的 $subcribe 属性来注册订阅者是不会影响上面的代码正常运行,也不会触发
     *
     * 事件订阅者是指那些在类本身中订阅多个事件的类，通过事件订阅者你可以在单个类中定义多个事件处理器。订阅者需要定义一个 subscribe 方法，该方法中传入一个事件分发器实例。你可以在给定的分发器中调用 listen 方法注册事件监听器
     *
     * 为订阅者注册监听器
     * @param Illuminate\Events\Dispatcher  $events
     */
    public function subscribe($events){//subscribe:订阅,认购,订购
        /*$events->listen(
            'Illuminate\Auth\Events\Login',
            'App\Listeners\UserEventSubscriber@onUserLogin'
        );

        $events->listen(
            'Illuminate\Auth\Events\Logout',
            'App\Listeners\UserEventSubscriber@onUserLogout'
        );*/

        $events->listen(
            'App\Events\EvevtQueueLab',//这里依然是事件
            'App\Listeners\QueueListener@onLab'//这里是监听器
        );
    }
}
